﻿#region

using System.Reflection;
using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Vin.Tool.AspNetCore.Extension;
using Vin.Tool.AsyncLocal;
using Vin.Tool.Attributes;
using Vin.Tool.Core.AssertCategory;
using Vin.Tool.Core.NetCategory;
using Vin.Tool.Domain.Config;
using Vin.Tool.Domain.ExceptionModel;
using Vin.Tool.Domain.SystemModel;

#endregion

namespace Vin.Tool.AspNetCore.Singleton;

public class VinApp
{
    #region 日志

    private static ILogger<VinApp>? _logger;

    private static ILogger<VinApp> Logger
    {
        get
        {
            if (_logger == null)
            {
                _logger = ServiceProvider.GetRequiredService<ILogger<VinApp>>();
            }

            return _logger;
        }
    }

    #endregion

    #region 基础数据

    /// <summary>
    /// 应用服务
    /// </summary>
    private static IServiceProvider _serviceProvider = null!;

    public static void SetServiceProvider(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
    public static IServiceProvider ServiceProvider => _serviceProvider;

    /// <summary>
    /// MVC构建器
    /// </summary>
    private static IMvcBuilder _mvcBuilder = null!;

    public static void SetMvcBuilder(IMvcBuilder mvcBuilder) => _mvcBuilder = mvcBuilder;
    public static IMvcBuilder MvcBuilder => _mvcBuilder;

    /// <summary>
    /// Web应用程序
    /// </summary>
    private static WebApplication _webApplication = null!;

    public static void SetWebApplication(WebApplication webApplication) => _webApplication = webApplication;
    public static WebApplication WebApplication => _webApplication;

    /// <summary>
    /// 全局配置构建器
    /// </summary>
    private static IConfiguration _configuration = null!;

    public static void SetConfiguration(IConfiguration configuration) => _configuration = configuration;
    public static IConfiguration Configuration => _configuration;

    /// <summary>
    /// 获取Web主机环境
    /// </summary>
    private static IHostEnvironment _webHostEnvironment = null!;

    public static void SetWebHostEnvironment(IHostEnvironment webHostEnvironment) =>
        _webHostEnvironment = webHostEnvironment;

    public static IHostEnvironment WebHostEnvironment => _webHostEnvironment;

    /// <summary>
    /// Vin Builder 初始化配置
    /// </summary>
    private static VinInitBuilderConfig _vinInitBuilderConfig = null!;

    public static void SetVinInitBuilderConfig(VinInitBuilderConfig vinInitBuilderConfig) =>
        _vinInitBuilderConfig = vinInitBuilderConfig;

    public static VinInitBuilderConfig VinInitBuilderConfig => _vinInitBuilderConfig;

    /// <summary>
    /// Vin Application 初始化配置
    /// </summary>
    private static VinUseApplicationConfig _vinUseApplicationConfig = null!;

    public static void SetVinUseApplicationConfig(VinUseApplicationConfig vinUseApplicationConfig) =>
        _vinUseApplicationConfig = vinUseApplicationConfig;

    public static VinUseApplicationConfig VinUseApplicationConfig => _vinUseApplicationConfig;

    /// <summary>
    /// 响应类型
    /// </summary>
    private static Type _apiResultType = typeof(ApiResult);

    public static void SetApiResultType<T>(T apiResult) where T : ApiResult => _apiResultType = typeof(T);

    public static Type ApiResultType => _apiResultType;

    #endregion

    #region 请求上下文

    public static IHttpContextAccessor? HttpContextAccessor => ServiceProvider.GetService<IHttpContextAccessor>();

    public static IAsyncServiceProvider? AsyncServiceProvider => ServiceProvider.GetService<IAsyncServiceProvider>();

    /// <summary>
    /// 获取请求上下文
    /// </summary>
    public static HttpContext HttpContext => HttpContextAccessor?.HttpContext!;

    public static HttpContext? HttpContextOrNull => HttpContextAccessor?.HttpContext;

    public static bool HaveHttpContext => HttpContextAccessor?.HttpContext != null;

    /// <summary>
    /// 获取请求上下文用户
    /// </summary>
    public static ClaimsPrincipal? User => HaveHttpContext ? HttpContext.User : null;

    public static HttpContextClientDetailModel HttpContextInfo(bool parseClient = false)
    {
        return HttpContext.GetClientDetail(parseClient);
    }

    /// <summary>
    /// 模拟一个HttpContext
    /// </summary>
    public static async Task SimulateHttpContext(Func<IServiceProvider, Task> action)
    {
        using (var scope = ServiceProvider.CreateScope())
        {
            var httpContextAccessor = scope.ServiceProvider.GetRequiredService<IHttpContextAccessor>();
            var httpContext = new DefaultHttpContext { RequestServices = scope.ServiceProvider };
            httpContextAccessor.HttpContext = httpContext;

            try
            {
                await action(scope.ServiceProvider);
            }
            finally
            {
                httpContextAccessor.HttpContext = null;
            }
        }
    }

    public static async Task CreateScopeAsync(Func<IServiceProvider, Task> action)
    {
        using (var scope = ServiceProvider.CreateScope())
        {
            var provider = scope.ServiceProvider;
            var asyncServiceProvider = scope.ServiceProvider.GetRequiredService<IAsyncServiceProvider>();
            asyncServiceProvider.ServiceProvider = provider;

            try
            {
                await action(provider);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "CreateScopeAsync");
                Console.WriteLine(e);
                throw;
            }
        }
    }

    #endregion

    #region 从根获取服务

    /// <summary>
    /// 获取服务
    /// </summary>
    /// <typeparam name="TService"></typeparam>
    /// <returns></returns>
    public static TService? GetService<TService>(string? serviceKey = null)
    {
        return (TService?)GetService(typeof(TService), serviceKey);
    }

    /// <summary>
    /// 获取服务
    /// </summary>
    /// <param name="type"></param>
    /// <param name="serviceKey"></param>
    /// <returns></returns>
    public static object? GetService(Type type, string? serviceKey = null)
    {
        // return ServiceProvider.GetService(type);
        return serviceKey.IsNotNullOrEmpty()
            ? GetKeyService(type, serviceKey)
            : ServiceProvider.GetService(type);
    }

    public static List<TService> GetServices<TService>(string? serviceKey = null)
        where TService : class
    {
        return GetServices(typeof(TService), serviceKey).Cast<TService>().ToList();
    }

    public static List<object?> GetServices(Type type, string? serviceKey = null)
    {
        // return ServiceProvider.GetServices(type).ToList();
        return serviceKey.IsNotNullOrEmpty()
            ? GetKeyServices(type, serviceKey)
            : ServiceProvider.GetServices(type).ToList();
    }

    /// <summary>
    /// 根据key获取服务
    /// </summary>
    /// <param name="type"></param>
    /// <param name="serviceKey"></param>
    /// <returns></returns>
    public static object? GetKeyService(Type type, string? serviceKey = null)
    {
        // var keyServiceProvider = ServiceProvider as IKeyedServiceProvider;
        // return keyServiceProvider?.GetKeyedService(type, serviceKey);
        return ServiceProvider.GetKeyedService(serviceKey, type);
    }

    public static TService? GetKeyService<TService>(string? serviceKey = null)
        where TService : class
    {
        return GetKeyService(typeof(TService), serviceKey) as TService;
    }

    public static List<TService> GetKeyServices<TService>(string? serviceKey = null)
        where TService : class
    {
        return GetKeyServices(typeof(TService), serviceKey).Cast<TService>().ToList();
    }

    public static List<object?> GetKeyServices(Type type, string? serviceKey = null)
    {
        return ServiceProvider.GetKeyedServices(type, serviceKey).ToList();
    }


    /// <summary>
    /// 获取服务
    /// </summary>
    /// <typeparam name="TService"></typeparam>
    /// <returns></returns>
    public static TService GetRequiredService<TService>()
        where TService : class
    {
        return (GetRequiredService(typeof(TService)) as TService)!;
    }

    /// <summary>
    /// 获取服务
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static object GetRequiredService(Type type)
    {
        return ServiceProvider.GetRequiredService(type);
    }

    #endregion

    #region 从请求上下文获取服务

    public static IServiceProvider GetRequestServiceProvider()
    {
        IServiceProvider? provider;
        if (HaveHttpContext)
        {
            provider = HttpContext.RequestServices;
        }
        else
        {
            if (AsyncServiceProvider!.ServiceProvider == null)
            {
                AsyncServiceProvider.ServiceProvider = ServiceProvider.CreateScope().ServiceProvider;
            }

            provider = AsyncServiceProvider.ServiceProvider;
        }

        // var provider = HaveHttpContext ? HttpContext.RequestServices : AsyncServiceProvider?.ServiceProvider;
        if (provider == null) throw new BusinessException("ServiceProvider is null");
        return provider;
    }

    #region 可选服务

    /// <summary>
    /// 获取请求生命周期的服务
    /// </summary>
    public static TService? GetServiceByHttpContext<TService>(string? serviceKey = null)
    {
        return serviceKey.IsNotNullOrEmpty()
            ? GetKeyServiceByHttpContext<TService>(serviceKey)
            : GetRequestServiceProvider().GetService<TService>();
    }

    public static IEnumerable<TService> GetServicesByHttpContext<TService>()
    {
        return GetRequestServiceProvider().GetServices<TService>();
    }

    public static TService GetRequiredKeyServiceByHttpContext<TService>(string? serviceKey = null)
    {
        return (TService)GetRequiredKeyServiceByHttpContext(typeof(TService), serviceKey);
    }

    /// <summary>
    /// 根据key获取服务
    /// </summary>
    public static TService? GetKeyServiceByHttpContext<TService>(string? serviceKey = null)
    {
        return GetRequestServiceProvider().GetKeyedService<TService>(serviceKey);
    }

    public static object GetRequiredKeyServiceByHttpContext(Type type, string? serviceKey = null)
    {
        return GetRequestServiceProvider().GetRequiredKeyedService(type, serviceKey);
    }

    public static IEnumerable<TService> GetRequiredKeyServicesByHttpContext<TService>(string? serviceKey = null)
    {
        return GetRequiredKeyServicesByHttpContext(typeof(TService), serviceKey).Cast<TService>();
    }

    public static IEnumerable<object?> GetRequiredKeyServicesByHttpContext(Type type, string? serviceKey = null)
    {
        return GetRequestServiceProvider().GetKeyedServices(type, serviceKey);
    }

    #endregion

    #region 必选服务

    /// <summary>
    /// 获取请求生命周期的服务
    /// </summary>
    public static TService GetRequiredServiceByHttpContext<TService>(string? serviceKey = null) where TService : notnull
    {
        return serviceKey.IsNotNullOrEmpty()
            ? GetRequiredKeyServiceByHttpContext<TService>(serviceKey)
            : GetRequestServiceProvider().GetRequiredService<TService>();
    }

    public static object GetRequiredServiceByHttpContext(Type type, string? serviceKey = null)
    {
        return serviceKey.IsNotNullOrEmpty()
            ? GetRequiredKeyServiceByHttpContext(type, serviceKey)
            : GetRequestServiceProvider().GetRequiredService(type);
    }

    public static IEnumerable<TService> GetRequiredServicesByHttpContext<TService>(string? serviceKey = null)
        where TService : notnull
    {
        return serviceKey.IsNotNullOrEmpty()
            ? GetRequiredKeyServicesByHttpContext<TService>(serviceKey)
            : GetRequestServiceProvider().GetServices<TService>();
    }

    public static IEnumerable<object?> GetRequiredServicesByHttpContext(Type type, string? serviceKey = null)
    {
        return serviceKey.IsNotNullOrEmpty()
            ? GetRequiredKeyServicesByHttpContext(type, serviceKey)
            : GetRequestServiceProvider().GetServices(type);
    }

    #endregion

    #endregion

    #region 自动注入

    /// <summary>
    /// 获取注入的属性信息
    /// </summary>
    private static readonly List<Func<Type, List<InjectModel>>> InjectProviderList = new();

    public static void AddInjectProvider(Func<Type, List<InjectModel>> injectProvider) =>
        InjectProviderList.Add(injectProvider);

    /// <summary>
    /// 属性注入
    /// </summary>
    /// <param name="instance"></param>
    /// <typeparam name="T"></typeparam>
    public static void Inject<T>(T instance)
    {
        if (instance == null) return;

        var instanceType = instance.GetType();

        var fieldInjectModels = DefaultInjectProvider(instanceType);

        if (InjectProviderList.Count > 0)
        {
            fieldInjectModels.AddRange(InjectProviderList.SelectMany(m => m(instanceType)));
        }

        Inject(instance, fieldInjectModels.DistinctBy(m => m.Type));
    }

    /// <summary>
    /// 默认获取注入属性
    /// </summary>
    /// <param name="instanceType"></param>
    /// <returns></returns>
    public static List<InjectModel> DefaultInjectProvider(Type instanceType)
    {
        var fieldInjectModels = instanceType
            .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
                       BindingFlags.FlattenHierarchy)
            .Where(m => m.GetCustomAttribute<InjectAttribute>() != null)
            .Select(field => new InjectModel()
            {
                InjectAttribute = field.GetCustomAttribute<InjectAttribute>()!,
                Type = field.FieldType,
                IsList = field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(List<>),
                IsArray = field.FieldType.IsArray,
                TypeModel = field
            })
            .ToList();

        var propertyInjectModels = instanceType
            .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
                           BindingFlags.FlattenHierarchy)
            .Where(m => m.GetCustomAttribute<InjectAttribute>() != null)
            .Select(property => new InjectModel()
            {
                InjectAttribute = property.GetCustomAttribute<InjectAttribute>()!,
                Type = property.PropertyType,
                IsList = property.PropertyType.IsGenericType &&
                         property.PropertyType.GetGenericTypeDefinition() == typeof(List<>),
                IsArray = property.PropertyType.IsArray,
                TypeModel = property
            });

        fieldInjectModels.AddRange(propertyInjectModels);

        return fieldInjectModels;
    }

    /// <summary>
    /// 属性注入
    /// </summary>
    /// <param name="instance"></param>
    /// <param name="fieldInjectModels"></param>
    /// <typeparam name="T"></typeparam>
    /// <exception cref="BusinessException"></exception>
    private static void Inject<T>(T instance, IEnumerable<InjectModel> fieldInjectModels)
    {
        if (instance == null) return;

        try
        {
            foreach (var injectModel in fieldInjectModels)
            {
                var key = injectModel.InjectAttribute.Key;
                if (injectModel.IsList)
                {
                    injectModel.SetListValue(instance,
                        GetRequiredServicesByHttpContext(injectModel.Type.GetGenericArguments()[0], key));
                }
                else if (injectModel.IsArray)
                {
                    injectModel.SetDefaultValue(instance,
                        GetRequiredServicesByHttpContext(injectModel.Type.GetElementType()!, key));
                }
                else
                {
                    injectModel.SetDefaultValue(instance, GetRequiredServiceByHttpContext(injectModel.Type, key));
                }
            }
        }
        catch (Exception ex)
        {
            throw new BusinessException(ex.Message);
        }
    }

    #endregion

    #region 获取日志

    public static ILogger<T> GetLogger<T>() => ServiceProvider.GetRequiredService<ILogger<T>>();

    public static ILogger GetLogger(Type type) =>
        ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger(type);

    public static ILogger GetLogger(object o) =>
        ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger(o.GetType());

    #endregion

    #region 获取配置

    public static T GetConfig<T>() => GetOptionsMonitor<T>().CurrentValue;
    public static T GetOption<T>() => GetOptionsMonitor<T>().CurrentValue;

    public static IOptionsMonitor<T> GetOptionsMonitor<T>() => ServiceProvider.GetRequiredService<IOptionsMonitor<T>>();

    #endregion
}